package com.basker.pisces.springcloud.feign.hystrix;

import java.lang.reflect.Method;
import java.util.HashMap;

import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;

import com.basker.pisces.service.annotation.ServiceConsume.IsolationStrategy;

import feign.Feign;

/**
 * 通过编码的方式设置的hystrix属性，其优先级仅高于默认值，优先读取配置文件中的属性
 *
 * @author hangwen
 */
public class HystrixCodingPropertySource extends MapPropertySource implements EnvironmentAware {

    private static final String HYSTRIX_PREFIX = "hystrix";
    private static final String HYSTRIX_COMMAND_TIMEOUT_ENABLE = "execution.timeout.enabled";
    private static final String HYSTRIX_COMMAND_TIMEOUT_MILLS = "execution.isolation.thread.timeoutInMilliseconds";
    private static final String HYSTRIX_COMMAND_ISOLATION_STRATEGY = "execution.isolation.strategy";
    private static final String HYSTRIX_COMMAND_SEMAPHORE_MAXCONCURRENTREQUESTS = "execution.isolation.semaphore" +
            ".maxConcurrentRequests";

    private Environment environment;

    public HystrixCodingPropertySource() {
        super("hystrixCodingPropertySource", new HashMap<>());
    }

    public void setCommandIsolationStrategy(Class<?> targetType, Method method, IsolationStrategy isolationStrategy) {
        this.setCommandProperty(targetType, method, HYSTRIX_COMMAND_ISOLATION_STRATEGY, isolationStrategy);
    }

    public void setCommandSemaphoreMaxConcurrentRequests(Class<?> targetType, Method method, int semaphoreMaxRequest) {
        this.setCommandProperty(targetType, method, HYSTRIX_COMMAND_SEMAPHORE_MAXCONCURRENTREQUESTS,
                semaphoreMaxRequest);
    }

    public void setCommandTimeoutEnable(Class<?> targetType, Method method, boolean enable) {
        this.setCommandProperty(targetType, method, HYSTRIX_COMMAND_TIMEOUT_ENABLE, enable);
    }

    public void setCommandTimeoutMills(Class<?> targetType, Method method, int timeoutMills) {
        this.setCommandProperty(targetType, method, HYSTRIX_COMMAND_TIMEOUT_MILLS, timeoutMills);
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;

        // 将自身添加到environment的PropertySources中，这样HystrixCommandProperties在初始化时可以从当前属性源中读取到配置的值
        if (this.environment instanceof ConfigurableEnvironment) {
            ((ConfigurableEnvironment) this.environment).getPropertySources().addLast(this);
        }
    }

    protected void setCommandProperty(Class<?> targetType, Method method, String propertyKey, Object value) {
        String key = generateCommandKey(targetType, method, propertyKey);
        this.source.put(key, value);
    }

    private String generateCommandKey(Class<?> targetType, Method method, String propertyKey) {
        return HYSTRIX_PREFIX + ".command." + Feign.configKey(targetType, method) + "." + propertyKey;
    }

}
